/*
 *
 * Copyright (C) 2008 Ed Hong
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */


#include "imgccd.h"
#include "SubbandBits.h"
#include "Characteristics.h"
#include "SubbandIterator.h"

static void printccdlines(FILE *out, const  Characteristic *c, int count) {
  fprintf(out, "#%s (%d): %s\n", c->name,count, c->description);
  fprintf(out, c->ccdline);
}


static void printccvalues(FILE *out, unsigned long long *charset, SubbandComponent *sc, 
			  int cn, 
			 int r, int c, int sb, int bp) {
  int i;
  NeighborSignificance nsig; 
  NeighborSigns nsign;
  Characteristic *ch;

  computeNeighborSignificance(&sc[cn], r,c,sb,bp, &nsig);
  computeNeighborSigns(&sc[cn], r,c,sb, bp, &nsign);

  for (i=0; i < TOTAL_NUM_CHARACTERISTICS; i++) {
    if (checkflag(i, charset)) {
      ch = &AllC[i];
      if (ch->f == NULL) {
	printf("printccvalues: function pointer for Characteristic %s does not exist; exitting..\n", ch->name);
	exit(1);
      }
      else {
	fprintf(out, " %d", 
		(*(ch->f))(sc,cn, &nsig, &nsign, r, c, sb, bp)
		);
      }
    }    
  }
  fprintf(out, "\n");
}



int WriteCCD(QccString filename, unsigned long long *charset, int numBitplanes) {
  FILE *outfile;

  if ((outfile = QccFileOpen(filename, "w")) == NULL)
   {
      QccErrorAddMessage("(WriteCCD): Error calling QccFileOpen()");
      return(1);
    }

  int i;
  fprintf(outfile, "#Characteristic Set is %llx %llx\n", charset[1], charset[0]);
  int count;
  count =0;
 
  for (i=0; i < TOTAL_NUM_CHARACTERISTICS; i++) {
    if (checkflag(i, charset)) {
      printccdlines(outfile, &AllC[i], count);
      count++;
    }
  }  
   
  QccFileClose(outfile);
  return(0);
}

int WriteCC(QccString filename, const QccWAVSubbandPyramid *sbp, int numComponents, int numBitplanes,
	    unsigned long long *charset, 
	    int (*f)(const CoeffHeapNode *, 
		     const CoeffHeapNode *,
		     const QccENTArithmeticModel *), 	    
	    unsigned long long *sortcharset, 
	    QccString sortctdfilename,
	    int debug ){
  FILE *outfile;
  
  if (filename[0] == '-' && filename[1] == '\0') {
     outfile = stdout; 
  }
  else {
   if ((outfile = QccFileOpen(filename, "w")) == NULL)
    {
      QccErrorAddMessage("(WriteCC): Error calling QccFileOpen()");
      return(1);
    }
  }
  
  SubbandComponent *sbits; 
  if ((sbits = (SubbandComponent *)
       malloc(sizeof(SubbandComponent)*numComponents)) == NULL)
    {
      QccErrorAddMessage("WriteCC: Error allocating memory");
      return(1);
    }

  double initialthresh;
  initialthresh = 1<<computeInitialThreshold(sbp, numComponents);

  int i;
  for (i=0; i < numComponents; i++) {
    if ( CreateBits(&sbp[i], initialthresh, &sbits[i], debug)){
      QccErrorAddMessage("(WriteCC): Error calling CreateBits()");
      return(1);    
    }  
  }
  
  // iterate through coefficients, printing out data as appropriate. 

  SubbandIterator si;
  contexttree ct;
  if ((sortctdfilename == NULL) || sortctdfilename[0]=='\0') 
    SubbandIterator_InitAlloc(&si, sbits, numComponents, NULL, NULL,NULL);
  else {
    if (readctdfile(sortctdfilename, sortcharset, &ct)){
      {
	QccErrorAddMessage("WriteCC: Error calling readctdfilename");
	return(1);
      }
    }

    if (SubbandIterator_InitAlloc(&si, sbits, numComponents, f, &ct,NULL)) {
	QccErrorAddMessage("WriteCC: Error calling SubbandIterator_InitAlloc");
	return(1);
    }
  }

  sbploc sl; 
  sl =SubbandIterator_curr(&si);
  while (sl.k < numBitplanes) {
    // value of bit
    int nextbit = bit_value(&sbits[sl.comp].sub[sl.subband], sl.i, sl.j, sl.k );
    fprintf(outfile,  "%d", nextbit);

    printccvalues(outfile, charset,sbits,sl.comp, sl.i,sl.j,sl.subband,sl.k);
    if (nextbit) update_descendant_siglevels(&sbits[sl.comp],sl.subband, sl.i, sl.j);

    int siglevel =  sbits[sl.comp].sub[sl.subband].coeff[sl.i][sl.j].significance_level;
    if (siglevel == sl.k) {
      // just became significant; print sign bit. 
      // use k== -1 for  sign bits. 
      fprintf(outfile,  "%d", bit_value(&sbits[sl.comp].sub[sl.subband], sl.i, sl.j, -1 ));
      printccvalues(outfile, charset,sbits,sl.comp, sl.i,sl.j,sl.subband,-1);
    }
    SubbandIterator_updateAdvance(&si);
    sl = SubbandIterator_curr(&si);
  }
  SubbandIterator_free(&si);

  // need to free memory for sbitsarray, which was created in CreateBits.
  //  also free memory for coeffdat arrays inside sbitsarray.  


  for (i=0; i < numComponents; i++) {
    SubbandComponentFree(&sbits[i]);
  }
  free(sbits);

  QccFileClose(outfile);
  return(0);
}

